/*******************************************************************************
 * SVN­Versioning-Information:
 * ­­­­­­­­­­­­­­­­­­­­­­­­
 * $LastChangedRevision: 6 $
 * $LastChangedBy: c.a.duevel $
 * $LastChangedDate: 2009-08-26 15:12:37 +0000 (Wed, 26 Aug 2009) $
 * $HeadURL: http://robofight.googlecode.com/svn/trunk/Server/src/de/hft_stuttgart/botwar/server/model/Robot.java $
 ******************************************************************************/
package de.hft_stuttgart.botwar.server.model;

import de.hft_stuttgart.botwar.common.interfaces.IRemotePlayer.MODE;
import de.hft_stuttgart.botwar.common.commands.ShootCmd;
import de.hft_stuttgart.botwar.common.models.Constants;
import de.hft_stuttgart.botwar.common.models.Position;
import java.util.Collection;

/**
 *
 * @author c.a.duevel@gmail.com
 */
class Robot {

    static final int RADIUS = 30; //in px just bot without shield
    static final int DIAMETER = 2 * RADIUS;
    private int health;
    Position target, userTarget, position;
    private RobotMode mode = new StandardMode();
    final RobotConfiguration config;
    Double distancePerRound;

    /**
     *
     * @param ID The ID of this robot
     * @param position The inital position of this robot
     * @param owner The owner of this robot - the usrID of a <code>player</code>
     */
    Robot(final RobotConfiguration robotConfiguration, final Position pos) {
        this.position = pos;
        this.config = robotConfiguration;
        this.health = robotConfiguration.getShield().getHealth();
    }

    /**
     * @return firepower of this robot
     */
    int getFirepower() {
        return config.getArmament().getFirePower();
    }

    int getID() {
        return config.getId();
    }

    /**
     * The user target is a target position assigned by the user.
     * @return the user target
     */
    Position getUserTarget() {
        return userTarget;
    }

    /**
     * Deletes the Usertarget Position
     */
    void deleteUserTarget() {
        this.userTarget = null;
    }

    /**
     * The user target is a target position assigned by the user.
     * @return the target to asssign to this robot
     */
    void setUserTarget(final Position userTarget, final String userID) {
        isAuthorized(userID);
        this.userTarget = userTarget;
        this.target = null;
    }

    /**
     * Returns the UserID
     * @return user ID of the owner of this robot
     */
    String getOWNER() {
        return config.getPlayer().getUsername();
    }

    /**
     * the automatic generated target
     * @return current target
     */
    Position getTarget() {
        return target;
    }

    /**
     * Returns the current RobotMode
     * @return current RobotMode
     */
    RobotMode getMode() {
        return mode;
    }

    /**
     * change the current RobotMode
     * @param mode new RobotMode
     */
    void setMode(final RobotMode mode) {
        this.mode = mode;
    }

    /**
     * returns the RobotID
     * @return RobotID of this robot
     */
    int getSpeed() {
        return config.getChassis().getSpeed();
    }

    /**
     * Returns the current health of this robot.
     * @return health
     */
    int getHealth() {
        return health;
    }

    /**
     * Updates the current health by the given difference
     * @param diffHealth the difference between the current health and the new
     */
    void updateHealth(final int diffHealth) {
        this.health += diffHealth;
    }

    boolean isDead() {
        return health <= 0;
    }

    void setTarget(final Position target) {
        this.target = target;
    }

     Position move(final double fraction) {
        if(fraction<0.0 || fraction>1.0){
            throw new IllegalArgumentException("Fraction of round can only" +
                    "must be between zero and one.");
        }
        if (isDead()) {
            throw new IllegalStateException("Robot is dead, can't move!");
        } else if (target == null || target.equals(position)) {
            return position;
        } else if (position.distance(target) <= getDistancePerRound()*fraction){
            this.position = target;
            return position;
        } else {
            double angle = Math.atan2(target.getY() - position.getY(),
                                      target.getX() - position.getX());
            double x = position.getX() + (double) (Math.cos(angle) *
                                         (getDistancePerRound()*fraction));
            double y = position.getY() + (double) (Math.sin(angle) *
                                         (getDistancePerRound()*fraction));
            position = new Position(x, y);
            return position;
        }
    }

    void setMode(final MODE mode, final String usrID) {
        isAuthorized(usrID);
        switch (mode) {
            case STANDARD:
                this.setMode(new StandardMode());
                break;
            case AGGRESSIVE:
                this.setMode(new AggressiveMode());
                break;
            default:
                throw new IllegalArgumentException("Illegal mode!");
        }
    }

    /**
     * This function updates the target of this robot after its current mode.
     * @param robots All the robots in the game.
     */
    public void updateTarget(final Collection<Robot> robots) {
        target = mode.getTarget(this, robots);
    }

    public Position getPos() {
        return new Position(position.getX(), position.getY());
    }

    ShootCmd fireAt(final Collection<Robot> robots) {
        Robot r = mode.fireAt(this, robots);
        if (r != null) {
            r.updateHealth(-this.getFirepower());
            return new ShootCmd(this.getID(), r.getID(), r.getHealth());
        } else {
            return null;
        }
    }

    double getDistance() {
        return config.getArmament().getRange();
    }

    @Override
    public String toString() {
        return (String.format("Robot %d at %s", this.getID(), this.getPos()));

    }

    void isAuthorized(final String usrID) {
        if (!usrID.equals(getOWNER())) {
            throw new UnsupportedOperationException("Users can perform this " +
                    "operation only on own robots.");
        }
    }

    double distanceTo(final Robot r) {
        return this.position.distance(r.position);
    }

    boolean isInRange(final Robot r) {
        return this.config.getArmament().getRange() >= this.distanceTo(r);
    }

    public boolean isEnemy(final Robot otherRobot) {
        return !isFriend(otherRobot);
    }

    public boolean isFriend(final Robot otherRobot) {
        return this.getOWNER().equals(otherRobot.getOWNER());
    }

    boolean collidesWith(final Robot r) {
        return this.distanceTo(r) <= DIAMETER ? true : false;
    }

    /**
     * @return distance this robot drives per round
     */
    public double getDistancePerRound() {
        if (distancePerRound == null) {
            distancePerRound = (this.getSpeed() *
                               (Constants.MS_PER_ROUND / 1000.0));
        }
        return distancePerRound.doubleValue();
    }

    double fractionOfRoundWithoutCrash(final Robot r2) {
        if (DIAMETER > this.distanceTo(r2)) {
            return 0.0;
        } else {
            return (this.distanceTo(r2) - DIAMETER) /
                    (this.getDistancePerRound() + r2.getDistancePerRound());
        }
    }
}
